package log

/*
gateway logger support GORM
@created by xiongwei

*/

import (
	"database/sql/driver"
	"fmt"
	"io"
	"reflect"
	"regexp"
	"time"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/sirupsen/logrus"
	rotatelogs "github.com/lestrrat-go/file-rotatelogs"
	"github.com/rifflock/lfshook"
)

var sqlRegexp = regexp.MustCompile(`(\$\d+)|\?`)
var defaultLogger *gormLogger

type LogFileFormatter struct {
}

// 格式化处理
func (l LogFileFormatter) Format(entry *logrus.Entry) ([]byte, error) {
	funcs := strings.SplitN(entry.Caller.Function, ".", 2)
	msg := fmt.Sprintf("[%s] [%s] [%s:%d] %s\n",
		entry.Time.Format("2006-01-02 15:04:05,000"),
		strings.ToUpper(entry.Level.String()),
		funcs[0]+"/"+filepath.Base(entry.Caller.File),
		entry.Caller.Line,
		entry.Message)
	return []byte(msg), nil
}
func InitLog(logPath string, logName string, maxSize int64, maxAge time.Duration) {
	filePaths := path.Join(logPath, logName)
	writer, _ := rotatelogs.New(
		filePaths+".%Y%m%d%H%M",            //分割后文件名
		rotatelogs.WithLinkName(filePaths), //软连接指向最新文件
		rotatelogs.WithMaxAge(maxAge),      // 最大保留时间(time.Hour*24*7) 一个星期
		//rotatelogs.WithRotationTime(rotaTime),     // 设置滚动时间
		rotatelogs.WithRotationSize(maxSize), //设置日志文件最大存储大小为10MB (1024*1024*10)
	)
	writeMap := lfshook.WriterMap{
		logrus.TraceLevel: writer,
		logrus.DebugLevel: writer,
		logrus.InfoLevel:  writer,
		logrus.WarnLevel:  writer,
		logrus.ErrorLevel: writer,
		logrus.FatalLevel: writer,
		logrus.PanicLevel: writer,
	}
	logrus.AddHook(lfshook.NewHook(writeMap, new(LogFileFormatter)))

	logrus.SetOutput(os.Stdout)
	logrus.SetReportCaller(true)
	logrus.SetFormatter(&LogFileFormatter{})
}

func init() {
	var logger = logrus.New()
	logger.Formatter.(*logrus.TextFormatter).ForceColors = true
	logger.Formatter.(*logrus.TextFormatter).FullTimestamp = true
	logger.Formatter.(*logrus.TextFormatter).QuoteEmptyFields = true
	defaultLogger = NewWithLogger(logger)
}

func GetDefaultGormLogger() *gormLogger {
	return defaultLogger
}

func SetLevel(level logrus.Level) {
	defaultLogger.Level = level
}

func SetOutPut(out io.Writer) {
	defaultLogger.SetOutput(out)
}

func Debugf(format string, args ...interface{}) {
	defaultLogger.Debugf(format, args...)
}

func Infof(format string, args ...interface{}) {
	defaultLogger.Infof(format, args...)
}

func Printf(format string, args ...interface{}) {
	defaultLogger.Printf(format, args...)
}

func Warnf(format string, args ...interface{}) {
	defaultLogger.Warnf(format, args...)
}

func Warningf(format string, args ...interface{}) {
	defaultLogger.Warningf(format, args...)
}

func Errorf(format string, args ...interface{}) {
	defaultLogger.Errorf(format, args...)
}

func Fatalf(format string, args ...interface{}) {
	defaultLogger.Fatalf(format, args...)
}

func Panicf(format string, args ...interface{}) {
	defaultLogger.Panicf(format, args...)
}

func Debug(args ...interface{}) {
	defaultLogger.Debug(args...)
}

func Info(args ...interface{}) {
	defaultLogger.Info(args...)
}

func Print(args ...interface{}) {
	defaultLogger.Print(args...)
}

func Warn(args ...interface{}) {
	defaultLogger.Warn(args...)
}

func Warning(args ...interface{}) {
	defaultLogger.Warning(args...)
}

func Error(args ...interface{}) {
	defaultLogger.Error(args...)
}

func Fatal(args ...interface{}) {
	defaultLogger.Fatal(args...)
}

func Panic(args ...interface{}) {
	defaultLogger.Panic(args...)
}

func Debugln(args ...interface{}) {
	defaultLogger.Debugln(args...)
}

func Infoln(args ...interface{}) {
	defaultLogger.Infoln(args...)
}
func Println(args ...interface{}) {
	defaultLogger.Println(args...)
}
func Warnln(args ...interface{}) {
	defaultLogger.Warnln(args...)
}
func Warningln(args ...interface{}) {
	defaultLogger.Warningln(args...)
}
func Errorln(args ...interface{}) {
	defaultLogger.Errorln(args...)
}
func Fatalln(args ...interface{}) {
	defaultLogger.Fatalln(args...)
}

func Panicln(args ...interface{}) {
	defaultLogger.Panicln(args...)
}

func WithField(key string, value interface{}) *logrus.Entry {
	return defaultLogger.WithField(key, value)
}

func WithFields(fields logrus.Fields) *logrus.Entry {
	return defaultLogger.WithFields(fields)
}

func WithError(err error) *logrus.Entry {
	return defaultLogger.WithError(err)
}

type gormLogger struct {
	name string
	*logrus.Logger
}

func (l *gormLogger) Print(values ...interface{}) {
	entry := l.WithField("name", l.name)
	if len(values) > 1 {
		level := values[0]
		source := values[1]
		entry = entry.WithField("source", source)
		if level == "sql" {
			duration := values[2]
			// sql
			var formattedValues []interface{}
			for _, value := range values[4].([]interface{}) {
				indirectValue := reflect.Indirect(reflect.ValueOf(value))
				if indirectValue.IsValid() {
					value = indirectValue.Interface()
					if t, ok := value.(time.Time); ok {
						formattedValues = append(formattedValues, fmt.Sprintf("'%v'", t.Format(time.RFC3339)))
					} else if b, ok := value.([]byte); ok {
						formattedValues = append(formattedValues, fmt.Sprintf("'%v'", string(b)))
					} else if r, ok := value.(driver.Valuer); ok {
						if value, err := r.Value(); err == nil && value != nil {
							formattedValues = append(formattedValues, fmt.Sprintf("'%v'", value))
						} else {
							formattedValues = append(formattedValues, "NULL")
						}
					} else {
						formattedValues = append(formattedValues, fmt.Sprintf("'%v'", value))
					}
				} else {
					formattedValues = append(formattedValues, fmt.Sprintf("'%v'", value))
				}
			}
			entry.WithField("took", duration).Debug(fmt.Sprintf(sqlRegexp.ReplaceAllString(values[3].(string), "%v"), formattedValues...))
		} else {
			entry.Error(values[2:]...)
		}
	} else {
		entry.Error(values...)
	}

}

// New Create new logger
func New() *gormLogger {
	return NewWithName("db")
}

// NewWithName Create new logger with custom name
func NewWithName(name string) *gormLogger {
	return NewWithNameAndLogger(name, logrus.StandardLogger())
}

// NewWithLogger Create new logger with custom logger
func NewWithLogger(logger *logrus.Logger) *gormLogger {
	return NewWithNameAndLogger("db", logger)
}

// NewWithNameAndLogger Create new logger with custom name and logger
func NewWithNameAndLogger(name string, logger *logrus.Logger) *gormLogger {
	return &gormLogger{name: name, Logger: logger}
}

func GetLevel() logrus.Level {
	return defaultLogger.Level
}
